package base

import "fmt"

/**
array、slice、map

 */

func Test3_001() {
	//array就是数组
	//var arr [n]type
	var arr [10]int // 声明了一个int类型的数组
	arr[0] = 42     // 数组下标是从0开始的
	arr[1] = 11     // 赋值操作
	arr[8] = 88
	fmt.Printf("The first element is %d yes\n", arr[0]) // 获取数据，返回42
	fmt.Printf("The last element is %d yes\n", arr[9])  //返回未赋值的最后一个元素，默认返回0

	fmt.Println(arr)

	//由于长度也是数组类型的一部分，因此[3]int与[4]int是不同的类型
	//数组之间的赋值是值的赋值，即当把一个数组作为参数传入函数的时候，传入的其实是该数组的副本，而不是它的指针
}

func Test3_002() {
	a := [3]int{1, 2, 3}   // 声明了一个长度为3的int数组
	b := [10]int{1, 2, 3}  // 声明了一个长度为10的int数组，其中前三个元素初始化为1、2、3，其它默认为0
	c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式，Go会自动根据元素个数来计算长度
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

func Test3_003() {
	// 声明了一个二维数组，该数组以两个数组作为元素，其中每个数组中又有4个int类型的元素
	doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}

	// 上面的声明可以简化，直接忽略内部的类型
	easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}

	fmt.Println(doubleArray)
	fmt.Println(easyArray)
}

/**
对于slice有几个有用的内置函数：
	len 获取slice的长度
	cap 获取slice的最大容量
	append 向slice里面追加一个或者多个元素，然后返回一个和slice一样类型的slice
	copy 函数copy从源slice的src中复制元素到目标dst，并且返回复制的元素的个数
 */

func Test3_004() {
	//“动态数组” 在Go里面这种数据结构叫slice
	//slice并不是真正意义上的动态数组，而是一个引用类型，slice总是指向一个底层array
	// 和声明array一样，只是少了长度

	var fslice []int
	fslice = append(fslice, 1)
	fslice = append(fslice, 34, 54, 23, 20, 88)
	//fmt.Println(fslice)

	//fmt.Println(len(fslice))
	//fmt.Println(cap(fslice))

	//??
	var copySlice []int
	count := copy(copySlice, fslice)
	fmt.Println(count)
	fmt.Println(copySlice)
}

func Test3_005() {
	slice := []byte{'a', 'b', 'c', 'd'}
	fmt.Println(slice)
	fmt.Println(slice[0])
	fmt.Println(slice[3])
}

func Test3_006() {
	// 声明一个含有10个元素元素类型为byte的数组
	var ar = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}

	// 声明两个含有byte的slice
	var a, b []byte

	// a指向数组的第3个元素开始，并到第五个元素结束，
	a = ar[2:5]
	//现在a含有的元素: ar[2]、ar[3]和ar[4]

	// b是数组ar的另一个slice
	b = ar[3:6]
	// b的元素是：ar[3]和ar[4]

	fmt.Println(a)
	fmt.Println(b)
}

func Test3_007() {
	// 声明一个数组
	var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
	// 声明两个slice
	var aSlice, bSlice []byte

	// 演示一些简便操作
	aSlice = array[:3] // 等价于aSlice = array[0:3] aSlice包含元素: a,b,c
	aSlice = array[5:] // 等价于aSlice = array[5:10] aSlice包含元素: f,g,h,i,j
	aSlice = array[:]  // 等价于aSlice = array[0:10] 这样aSlice包含了全部的元素

	// 从slice中获取slice
	aSlice = array[3:7]  // aSlice包含元素: d,e,f,g，len=4，cap=7
	bSlice = aSlice[1:3] // bSlice 包含aSlice[1], aSlice[2] 也就是含有: e,f
	bSlice = aSlice[:3]  // bSlice 包含 aSlice[0], aSlice[1], aSlice[2] 也就是含有: d,e,f
	bSlice = aSlice[0:5] // 对slice的slice可以在cap范围内扩展，此时bSlice包含：d,e,f,g,h
	bSlice = aSlice[:]   // bSlice包含所有aSlice的元素: d,e,f,g
	fmt.Println(bSlice)
}

/**
map
 */
func Test3_008() {
	// 声明一个key是字符串，值为int的字典,这种方式的声明需要在使用之前使用make初始化
	var numbers map[string]int
	numbers = make(map[string]int)

	numbers["one"] = 1  //赋值
	numbers["ten"] = 10 //赋值
	numbers["three"] = 3

	fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据
	// 打印出来如:第三个数字是: 3
}

func Test3_009() {
	// 另一种map的声明方式
	numbers2 := make(map[string]string)
	numbers2["a"] = "hello"
	numbers2["b"] = "world"
	fmt.Println(numbers2)
}

/**
使用map过程中需要注意的几点：
map是无序的，每次打印出来的map都会不一样，它不能通过index获取，而必须通过key获取
map的长度是不固定的，也就是和slice一样，也是一种引用类型
内置的len函数同样适用于map，返回map拥有的key的数量
map的值可以很方便的修改，通过numbers["one"]=11可以很容易的把key为one的字典值改为11
map和其他基本型别不同，它不是thread-safe，在多个go-routine存取时，必须使用mutex lock机制
 */

func Test3_010() {
	// 初始化一个字典
	rating := map[string]float32{"C": 5, "Go": 4.5, "Python": 4.5, "C++": 2}
	// map有两个返回值，第二个返回值，如果不存在key，那么ok为false，如果存在ok为true
	csharpRating, ok := rating["C#"]
	if ok {
		fmt.Println("C# is in the map and its rating is ", csharpRating)
	} else {
		fmt.Println("We have no rating associated with C# in the map")
	}

	delete(rating, "C") // 删除key为C的元素
	csharpRating, ok = rating["C"]
	fmt.Println(csharpRating, ok)
}

func Test3_011() {
	m := make(map[string]string)
	m["Hello"] = "Bonjour"
	m1 := m
	m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了
}

func Test3_012() {
	// make用于内建类型（map、slice 和channel）的内存分配
	// new用于各种类型的内存分配

	// 内建函数new本质上说跟其它语言中的同名函数功能一样：new(T)分配了零值填充的T类型的内存空间，并且返回其地址，即一个*T类型的值
	// 用Go的术语说，它返回了一个指针，指向新分配的类型T的零值
	// new 返回指针

	// 内建函数make(T, args)与new(T)有着不同的功能，
	// make只能创建slice、map和channel，
	// 并且返回一个有初始值(非零)的T类型，而不是*T

	//本质来讲，导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化

	//例如，一个slice，是一个包含指向数据（内部array）的指针、长度和容量的三项描述符；
	// 在这些项目被初始化之前，slice为nil。
	// 对于slice、map和channel来说，make初始化了内部的数据结构，填充适当的值。

	//make返回初始化后的（非零）值
}

func Test3_013() {}

func Test3_014() {}
